package com.congee02.spring.core;

/**
 * 用于使用根本原因包装运行时 {@code Exceptions} 的便捷类。
 * @author congee(congee02 @ 163.com)
 * @date 1/28/2023 3:35 PM
 */
public abstract class NestedRuntimeException extends RuntimeException {

    private static final long serialVersionUID = 3381676479274754563L;

    static {
        /*急切加载 NestedExceptionUtils 类以避免调用 getMessage() 时 OSGi 上的类加载器死锁问题。*/
        NestedExceptionUtils.class.getName();
    }

    /**
     * 使用指定的详细消息构造 {@code NestedRuntimeException}。
     * @param msg  详细信息
     */
    public NestedRuntimeException(String msg) {
        super(msg);
    }

    /**
     * 使用指定的消息和根本原因创建一个新的 NestedRuntimeException
     * @param msg   详细信息
     * @param cause 嵌套异常
     */
    public NestedRuntimeException(String msg, Throwable cause) {
        super(msg, cause);
    }

    /**
     * 返回详细消息，包括来自嵌套异常的消息（如果有的话）。
     */
    @Override
    public String getMessage() {
        return NestedExceptionUtils.buildMessage(super.getMessage(), getCause());
    }

    /**
     * 检索此异常的最根本原因（如果有）。
     * @return  最根本的异常，如果没有则返回 {@code null}
     */
    public Throwable getRootCause() {
        return NestedExceptionUtils.getRootCause(this);
    }

    /**
     * 检索此异常的最具体原因，即最内在的原因（根本原因）或此异常本身  
     * 与 {@link #getRootCause()} 的不同之处在于，如果没有根本原因，它会回退到当前异常。
     * @return 最具体的原因（永远不返回 {@code null}）
     */
    public Throwable getMostSpecificCause() {
        Throwable rootCause = getRootCause();
        return (rootCause != null ? rootCause : this);
    }

    /**
     * 检查此异常是否包含给定类型的异常：它本身属于给定类，或者它包含给定类型的嵌套原因。
     * @param exType 要查找的异常类型
     * @return  是否存在指定类型的嵌套异常
     */
    public boolean contains(Class<?> exType) {
        if (exType == null) {
            return false;
        }
        if (exType.isInstance(this)) {
            return true;
        }
        Throwable cause = getCause();
        if (cause == this) {
            return false;
        }
        if (cause instanceof NestedRuntimeException) {
            return  ((NestedRuntimeException) cause).contains(exType);
        }
        else {
            while (cause != null) {
                if (exType.isInstance(cause)) {
                    return true;
                }
                if (cause.getCause() == cause) {
                    break;
                }
                cause = cause.getCause();
            }
            return false;
        }
    }


}
